#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
本地Web仪表板 - 连接虚拟机Redis服务
====================================

本模块是一个Flask Web应用，用于连接虚拟机上的Redis服务，提供农产品价格数据的
实时监控和可视化展示功能。

主要功能：
1. 连接虚拟机Redis服务，获取农产品价格数据
2. 提供RESTful API接口，支持数据查询和统计
3. 实时监控爬虫运行状态和数据更新情况
4. 提供系统健康检查和状态监控

作者: 系统开发团队
版本: 1.0.0
日期: 2024
"""

# 导入必要的库
from flask import Flask, jsonify, request, render_template  # Flask核心组件
import redis  # Redis客户端库，用于连接Redis数据库
import json  # JSON数据处理
import logging  # 日志记录
from datetime import datetime, timedelta  # 日期时间处理

# 创建Flask应用实例
app = Flask(__name__)

# ==================== 日志配置 ====================
# 配置日志记录，设置日志级别为INFO
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# ==================== 虚拟机服务配置 ====================
# 虚拟机Redis服务的连接配置信息
VM_CONFIG = {
    'redis_host': '192.168.88.130',  # 虚拟机IP地址
    'redis_port': 6379,  # Redis端口号
    'redis_db': 0,  # Redis数据库编号
    'redis_password': '123456',  # Redis访问密码
    'spider_api_port': 5000  # 爬虫API服务端口
}


class RemoteDataManager:
    """
    远程数据管理器类

    负责连接虚拟机上的Redis服务，获取和管理农产品价格数据。
    提供数据查询、统计分析和系统状态监控功能。
    """

    def __init__(self):
        """
        初始化数据管理器

        创建Redis客户端连接，并尝试连接到虚拟机Redis服务
        """
        self.redis_client = None  # Redis客户端实例
        self.connect_redis()  # 连接Redis服务

    def connect_redis(self):
        """
        连接虚拟机Redis服务

        建立与虚拟机Redis数据库的连接，设置连接参数和超时时间。
        连接成功后记录日志，失败时记录错误信息。
        """
        try:
            # 创建Redis客户端连接
            self.redis_client = redis.Redis(
                host=VM_CONFIG['redis_host'],  # Redis主机地址
                port=VM_CONFIG['redis_port'],  # Redis端口
                db=VM_CONFIG['redis_db'],  # 数据库编号
                password=VM_CONFIG['redis_password'],  # 访问密码
                decode_responses=True,  # 自动解码响应
                socket_timeout=10,  # 套接字超时时间
                socket_connect_timeout=10  # 连接超时时间
            )
            # 测试连接是否成功
            self.redis_client.ping()
            logger.info("✓ 成功连接到虚拟机Redis服务")
        except Exception as e:
            # 连接失败时记录错误信息
            logger.error(f"✗ 连接虚拟机Redis失败: {e}")
            self.redis_client = None

    def get_latest_prices(self, limit=50):
        """
        获取最新的价格数据

        参数:
            limit (int): 返回数据的最大数量，默认50条

        返回:
            list: 价格数据列表，按时间戳倒序排列
        """
        # 检查Redis连接是否可用
        if not self.redis_client:
            return []

        try:
            # 获取所有价格数据的键名
            keys = self.redis_client.keys('cnhnb_price:*')
            if not keys:
                return []

            data_list = []
            # 遍历所有键，获取对应的数据
            for key in keys:
                try:
                    data = self.redis_client.get(key)
                    if data:
                        item = json.loads(data)  # 解析JSON数据
                        data_list.append(item)
                except Exception as e:
                    # 数据解析失败时记录警告
                    logger.warning(f"解析数据失败 {key}: {e}")

            # 按时间戳倒序排序
            data_list.sort(key=lambda x: x.get('timestamp', 0), reverse=True)

            # 去重处理：基于产品名称、价格、产地和日期的组合
            seen = set()
            unique_data = []
            for item in data_list:
                # 创建唯一标识键
                uniq_key = (item.get('name'), item.get('price_value'),
                            item.get('location'), item.get('date'))
                if uniq_key not in seen:
                    seen.add(uniq_key)
                    unique_data.append(item)
                # 达到限制数量时停止
                if len(unique_data) >= limit:
                    break

            return unique_data

        except Exception as e:
            # 获取数据失败时记录错误
            logger.error(f"获取价格数据失败: {e}")
            return []

    def get_system_status(self):
        """
        获取系统状态信息

        检查Redis连接状态、爬虫运行状态、数据统计信息等。

        返回:
            dict: 包含系统状态信息的字典
        """
        # 初始化状态信息
        status = {
            'redis_connected': False,  # Redis连接状态
            'spider_running': False,  # 爬虫运行状态
            'data_count': 0,  # 数据总数
            'last_update': None  # 最后更新时间
        }

        try:
            if self.redis_client:
                # 测试Redis连接
                self.redis_client.ping()
                status['redis_connected'] = True

                # 获取数据总数
                keys = self.redis_client.keys('cnhnb_price:*')
                status['data_count'] = len(keys)

                if keys:
                    # 获取最新的数据键
                    latest_key = max(keys, key=lambda k: self.redis_client.get(k))
                    latest_data = self.redis_client.get(latest_key)

                    if latest_data:
                        item = json.loads(latest_data)
                        status['last_update'] = item.get('crawl_timestamp')

                        # 检查爬虫是否在运行（基于最后更新时间）
                        if item.get('crawl_timestamp'):
                            try:
                                last_time = datetime.fromisoformat(item['crawl_timestamp'])
                                # 如果最后更新时间在1分钟内，认为爬虫正在运行
                                if datetime.now() - last_time < timedelta(minutes=1):
                                    status['spider_running'] = True
                            except Exception:
                                pass

        except Exception as e:
            logger.error(f"检查Redis状态失败: {e}")

        return status


# 创建全局数据管理器实例
data_manager = RemoteDataManager()


# ==================== Flask路由定义 ====================

@app.route('/')
def index():
    """
    主页路由

    返回Web仪表板的主页面，提供数据可视化和监控界面。

    返回:
        str: 渲染后的HTML页面
    """
    return render_template('dashboard.html')


@app.route('/api/latest_prices')
def api_latest_prices():
    """
    最新价格数据API接口

    获取最新的农产品价格数据，支持分页查询。

    返回:
        JSON: 包含价格数据和统计信息的JSON响应
    """
    # 获取所有价格数据（限制10万条）
    all_data = data_manager.get_latest_prices(limit=100000)

    return jsonify({
        'success': True,  # 请求成功标志
        'data': all_data,  # 价格数据列表
        'count': len(all_data),  # 数据总数
        'timestamp': datetime.now().isoformat()  # 响应时间戳
    })


@app.route('/api/system_status')
def api_system_status():
    """
    系统状态API接口

    获取Redis连接状态、爬虫运行状态等系统信息。

    返回:
        JSON: 包含系统状态信息的JSON响应
    """
    status = data_manager.get_system_status()

    return jsonify({
        'success': True,  # 请求成功标志
        'data': status,  # 系统状态数据
        'timestamp': datetime.now().isoformat()  # 响应时间戳
    })


@app.route('/api/health')
def health_check():
    """
    健康检查API接口

    检查系统健康状态，主要用于监控和负载均衡。

    返回:
        JSON: 包含健康状态信息的JSON响应
    """
    # 检查Redis连接状态
    redis_connected = data_manager.redis_client is not None
    try:
        if redis_connected:
            data_manager.redis_client.ping()  # 测试连接
    except:
        redis_connected = False

    return jsonify({
        'success': True,  # 请求成功标志
        'status': 'connected' if redis_connected else 'disconnected',  # 连接状态
        'redis_status': 'connected' if redis_connected else 'disconnected',  # Redis状态
        'timestamp': datetime.now().isoformat()  # 响应时间戳
    })


@app.route('/api/statistics')
def api_statistics():
    """
    统计数据API接口

    计算农产品价格的统计信息，包括平均价格、最高最低价格等。

    返回:
        JSON: 包含统计信息的JSON响应
    """
    # 获取所有价格数据
    all_data = data_manager.get_latest_prices(limit=100000)
    stats = {}  # 统计信息字典
    recent_count_24h = 0  # 24小时内数据计数
    now = datetime.now()

    # 遍历所有数据，计算统计信息
    for item in all_data:
        name = item.get('name')  # 产品名称
        price = float(item.get('price_value') or 0)  # 价格
        location = item.get('location')  # 产地
        ts_str = item.get('crawl_timestamp')  # 爬取时间戳

        # 跳过无效数据
        if not name or not price:
            continue

        # 初始化产品统计信息
        if name not in stats:
            stats[name] = {
                'count': 0,  # 数据条数
                'total_price': 0,  # 总价格
                'locations': set(),  # 产地集合
                'max_price': price,  # 最高价格
                'min_price': price  # 最低价格
            }

        # 更新统计信息
        stat = stats[name]
        stat['count'] += 1
        stat['total_price'] += price
        stat['locations'].add(location)
        stat['max_price'] = max(stat['max_price'], price)
        stat['min_price'] = min(stat['min_price'], price)

        # 统计24小时内的数据
        try:
            ts = datetime.fromisoformat(ts_str)
            if now - ts < timedelta(hours=24):
                recent_count_24h += 1
        except:
            pass

    # 计算最终统计结果
    result = {}
    for name, stat in stats.items():
        avg_price = round(stat['total_price'] / stat['count'], 2)  # 计算平均价格
        result[name] = {
            'avg_price': avg_price,  # 平均价格
            'count': stat['count'],  # 数据条数
            'locations_count': len(stat['locations']),  # 产地数量
            'max_price': stat['max_price'],  # 最高价格
            'min_price': stat['min_price']  # 最低价格
        }

    return jsonify({
        'success': True,  # 请求成功标志
        'statistics': result,  # 统计信息
        'recent_count_24h': recent_count_24h  # 24小时内数据数量
    })


@app.route('/api/products')
def api_products():
    """
    产品信息API接口

    获取所有农产品的汇总信息，包括最新价格、平均价格、价格范围等。

    返回:
        JSON: 包含产品信息的JSON响应
    """
    # 获取所有价格数据
    all_data = data_manager.get_latest_prices(limit=100000)
    product_map = {}  # 产品信息映射

    # 遍历数据，汇总产品信息
    for item in all_data:
        name = item.get('name')  # 产品名称
        price = float(item.get('price_value') or 0)  # 价格
        location = item.get('location')  # 产地

        # 跳过无效数据
        if not name or not price:
            continue

        # 初始化产品信息
        if name not in product_map:
            product_map[name] = {
                'name': name,  # 产品名称
                'latest_price': price,  # 最新价格
                'avg_price': 0,  # 平均价格
                'count': 0,  # 数据条数
                'total_price': 0,  # 总价格
                'max_price': price,  # 最高价格
                'min_price': price,  # 最低价格
                'locations': set()  # 产地集合
            }

        # 更新产品信息
        stat = product_map[name]
        stat['latest_price'] = price  # 更新最新价格
        stat['count'] += 1
        stat['total_price'] += price
        stat['locations'].add(location)
        stat['max_price'] = max(stat['max_price'], price)
        stat['min_price'] = min(stat['min_price'], price)

    # 构建产品列表
    products = []
    for name, stat in product_map.items():
        avg_price = round(stat['total_price'] / stat['count'], 2)  # 计算平均价格
        price_range = f"{stat['min_price']} - {stat['max_price']}"  # 价格范围

        products.append({
            'name': name,  # 产品名称
            'latest_price': stat['latest_price'],  # 最新价格
            'avg_price': avg_price,  # 平均价格
            'price_range': price_range,  # 价格范围
            'locations_count': len(stat['locations']),  # 产地数量
            'data_count': stat['count']  # 数据条数
        })

    # 按数据条数倒序排序
    return jsonify({
        'success': True,  # 请求成功标志
        'products': sorted(products, key=lambda x: x['data_count'], reverse=True)  # 产品列表
    })


# ==================== 应用启动入口 ====================

if __name__ == '__main__':
    import argparse

    # 创建命令行参数解析器
    parser = argparse.ArgumentParser(description='本地惠农网价格数据Web仪表板')
    parser.add_argument('--host', default='0.0.0.0', help='监听地址')
    parser.add_argument('--port', default=8080, type=int, help='监听端口')
    parser.add_argument('--debug', action='store_true', help='调试模式')

    # 解析命令行参数
    args = parser.parse_args()

    # 打印启动信息
    print("=" * 60)
    print("本地惠农网农产品价格监控Web仪表板")
    print("=" * 60)
    print(f"虚拟机Redis地址: {VM_CONFIG['redis_host']}:{VM_CONFIG['redis_port']}")
    print(f"本地Web地址: http://localhost:{args.port}")
    print("=" * 60)

    # 启动Flask应用
    app.run(
        host=args.host,  # 监听地址
        port=args.port,  # 监听端口
        debug=args.debug,  # 调试模式
        threaded=True  # 启用多线程
    )
